package org.example.web;

import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.PutObjectResult;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
import lombok.extern.slf4j.Slf4j;
import org.example.mapper.ActResultLogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;

/**
 * web读写案例
 *
 * @author Jiaju Zhuang
 **/
@Slf4j
@Controller
public class WebTest {

    @Autowired
    private UploadDAO uploadDAO;

    @Autowired
    private ExportTaskServiceImpl exportTaskService;

    @Autowired
    private ActResultLogMapper actResultLogMapper;

    @Resource(name = "taskExecutor")
    private ThreadPoolExecutor threadPoolExecutor;

    public static void main3(String[] args) {

        // 您的工程中可以只保留一个全局的ObsClient实例
// ObsClient是线程安全的，可在并发场景下使用
        ObsClient obsClient = null;
        try {
            String endPoint = "http://obs.cn-east-3.myhuaweicloud.com";
            String ak = "EWGYXHBAMGH2GMUTGFFZ";
            String sk = "477LboFMzhzHPvPMN4p4B5fDopUhdCON0WsC49QB";
            // 创建ObsClient实例
            obsClient = new ObsClient(ak, sk, endPoint);
            // 调用接口进行操作，例如上传对象
            PutObjectResult response = obsClient.putObject("dev-test-obs", "测试.xlsx", new File("D:\\google download\\测试.xlsx"));  // localfile为待上传的本地文件路径，需要指定到具体的文件名
            System.out.println(response);
        } catch (ObsException e) {
            System.out.println("HTTP Code: " + e.getResponseCode());
            System.out.println("Error Code:" + e.getErrorCode());
            System.out.println("Error Message: " + e.getErrorMessage());

            System.out.println("Request ID:" + e.getErrorRequestId());
            System.out.println("Host ID:" + e.getErrorHostId());
        } finally {
            // 关闭ObsClient实例，如果是全局ObsClient实例，可以不在每个方法调用完成后关闭
            // ObsClient在调用ObsClient.close方法关闭后不能再次使用
            if (obsClient != null) {
                // obsClient.close();
            }
        }

//        try {
//            URL url = new URL("https://dev-test-obs.obs.cn-east-3.myhuaweicloud.com/web/%E6%B5%8B%E8%AF%95.xlsx");
//            URLConnection connection = url.openConnection();
//            InputStream is = connection.getInputStream();
//            BufferedReader br = new BufferedReader(new InputStreamReader(is, "gb2312"));
//            System.out.println(br);
//        } catch (MalformedURLException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

    }

    /**
     * 文件下载（失败了会返回一个有部分数据的Excel）
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link DownloadData}
     * <p>
     * 2. 设置返回的 参数
     * <p>
     * 3. 直接写，这里注意，finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
     */
    @GetMapping("download")
    public void download(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
    }

    /**
     * 分页下载
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("pageDownload")
    public void pageDownload(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("response.setContentType(\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\")/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 方法1: 如果写到同一个sheet
        // 这里 需要指定写用哪个class去写
//        try (
//                ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), DownloadData.class).build()) {
//            // 这里注意 如果同一个sheet只要创建一次
//            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
//            // 去调用写入,这里我调用了五次，实际使用时根据数据库分页的总的页数来
//            for (int i = 0; i < 5; i++) {
//                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
//                List<DownloadData> data = data();
//                excelWriter.write(data, writeSheet);
//            }
//
//        }


        // 方法2: 如果写到不同的sheet 同一个对象
        // 这里 指定文件
//        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), DownloadData.class).build()) {
//            // 去调用写入,这里我调用了五次，实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
//            for (int i = 0; i < 5; i++) {
//                // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
//                WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();
//                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
//                List<DownloadData> data = data();
//                excelWriter.write(data, writeSheet);
//            }
//        }
            // 方法3 如果写到不同的sheet 不同的对象
            // 这里 指定文件
            try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(),DownloadData.class).build()) {
                // 去调用写入,这里我调用了五次，实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
                for (int i = 0; i < 5; i++) {
                    // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。这里注意DemoData.class 可以每次都变，我这里为了方便 所以用的同一个class
                    // 实际上可以一直变
                    WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).head(DownloadData.class).build();
                    // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                    List<DownloadData> data = data();
                    excelWriter.write(data, writeSheet);
                }
            }




    }

    /**
     * 分页下载分sheet4
     *
     * @param
     * @throws IOException
     */
    @GetMapping("testWrite1")
    public void TestWrite1(HttpServletResponse response) throws IOException {
        response.setContentType("response.setContentType(\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\")/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
//        String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");

        //获取要导出的数据
        List<Student> fillData = initFillData();
        //获取一共多少条数据
        int totalCount = fillData.size();
        String fileName = "学生信息报告.xlsx";
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        //单表最多存储数
        int sheetMaxNum = 1048576;

        ExcelWriter excelWriter = null;
        // 如果总数超出单个sheet的大小 1048575 则进行分页
        if (totalCount > sheetMaxNum) {
            excelWriter = EasyExcel.write(response.getOutputStream(), Student.class).build();
            // 设置的单个Sheet的数据量，必须小于1048576，
            int pageSize = 1048575;
            //要分表的页数
            int sheetCount = 0;
            //如果能整除则为整除的数
            if (totalCount % pageSize == 0) {
                sheetCount = totalCount / pageSize;
            } else {
                //如果不能整除则+1
                sheetCount = (totalCount / pageSize) + 1;
            }
            int num = 0;
            //进行循环分表
            for (int pageNum = 1; pageNum <= sheetCount; pageNum++) {
                WriteSheet writeSheet = EasyExcel.writerSheet(pageNum, "学生信息Sheet表_" + pageNum).head(Student.class).build();

                int fromIndex = pageSize * pageNum;
                if (fromIndex >= totalCount) {
                    fromIndex = totalCount;
                }
                //将数据进行分组
                excelWriter.write(fillData.subList(num, fromIndex), writeSheet);
                num = fromIndex;
            }
        } else { // 不超出则一次性全部导出
            excelWriter = EasyExcel.write(fileName, Student.class).build();
            WriteSheet writeSheet = EasyExcel.writerSheet("学生信息表").head(Student.class).build();
            excelWriter.write(initFillData(), writeSheet);
        }
        excelWriter.finish();
    }


    //测试导出两百万条数据
    private static List<Student> initFillData() {
        ArrayList<Student> fillDatas = new ArrayList<Student>();
        for (int i = 2; i < 2000000; i++) {
            Student fillData = new Student();
            fillData.setAge(i);
            fillDatas.add(fillData);
        }
        return fillDatas;
    }

    /**
     * 分页下载，上传到oss，返回地址，建议这样搞
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("downloadOssUrl")
    @ResponseBody
    public PutObjectResult downloadOssUrl(HttpServletResponse response) throws IOException {
        String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (
                ExcelWriter excelWriter = EasyExcel.write(out, DownloadData.class).build()) {
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            // 去调用写入,这里我调用了五次，实际使用时根据数据库分页的总的页数来
            for (int i = 0; i < 5; i++) {
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<DownloadData> data = data();
                excelWriter.write(data, writeSheet);
            }
        }

// 文件上传到oss
        InputStream fileInputStream = new ByteInputStream(out.toByteArray(), out.size());
//        MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
        ObsClient obsClient = null;
        PutObjectResult response1 = null;
        try {
            String endPoint = "http://obs.cn-east-3.myhuaweicloud.com";
            String ak = "EWGYXHBAMGH2GMUTGFFZ";
            String sk = "477LboFMzhzHPvPMN4p4B5fDopUhdCON0WsC49QB";
            // 创建ObsClient实例
            obsClient = new ObsClient(ak, sk, endPoint);
            // 调用接口进行操作，例如上传对象
            response1 = obsClient.putObject("dev-test-obs", "测试.xlsx", fileInputStream);  // localfile为待上传的本地文件路径，需要指定到具体的文件名
            System.out.println(response1);
        } catch (ObsException e) {
            System.out.println("HTTP Code: " + e.getResponseCode());
            System.out.println("Error Code:" + e.getErrorCode());
            System.out.println("Error Message: " + e.getErrorMessage());

            System.out.println("Request ID:" + e.getErrorRequestId());
            System.out.println("Host ID:" + e.getErrorHostId());
        } finally {
            // 关闭ObsClient实例，如果是全局ObsClient实例，可以不在每个方法调用完成后关闭
            // ObsClient在调用ObsClient.close方法关闭后不能再次使用
            if (obsClient != null) {
                // obsClient.close();
            }
        }
        return response1;
    }


    /**
     * 先记录日志，然后异步导出，文件上传到obs,返回。（这个只是异步版本，不是多线程版本）目前还差一个就是多线程的导出方法
     *
     * @param response
     * @throws IOException
     */

    @GetMapping("dataExport300wOss")
    public void dataExport300wOss(HttpServletResponse response) throws IOException {
        //先插入一条记录，然后在导出完成的最后，上传到oss保存该记录
        ExportTask exportTask = new ExportTask();
        exportTask.setTaskName("前台系统传或者后台自定义");
        exportTask.setDr(0);
        exportTask.setCreateTime(new Date());
        exportTask.setCreatePerson("1");
        exportTask.setTaskParams("可以通过request获得");
        exportTask.setStatus(0);
        exportTask.setUserId(1L);
        exportTaskService.save(exportTask);
        HashMap<String, String> hashMap = new HashMap<String, String>();
        {

            long startTime = System.currentTimeMillis();
            System.out.println("导出开始时间:" + startTime);
            String fileName = new String(("excel100w").getBytes(), "UTF-8");

            //title
//            Table table = new Table(1);
//            List<List<String>> titles = new ArrayList<List<String>>();
//            titles.add(Arrays.asList("onlineseqid"));
//            titles.add(Arrays.asList("businessid"));
//            titles.add(Arrays.asList("becifno"));
//            titles.add(Arrays.asList("ivisresult"));
//            titles.add(Arrays.asList("createdby"));
//            titles.add(Arrays.asList("createddate"));
//            titles.add(Arrays.asList("updateby"));
//            titles.add(Arrays.asList("updateddate"));
//            titles.add(Arrays.asList("risklevel"));
//            table.setHead(titles);

            //模拟统计查询的数据数量这里模拟100w
            int count = 3000001;
            //记录总数:实际中需要根据查询条件进行统计即可
            Integer totalCount = actResultLogMapper.findActResultLogByCondations(count);
            totalCount = count;
            //每一个Sheet存放100w条数据
            Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;
            //每次写入的数据量
            Integer writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;
            //计算需要的Sheet数量
            Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            //计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
            Integer oneSheetWriteCount = sheetDataRows / writeDataRows;
            //计算最后一个sheet需要写入的次数
            Integer lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) : (totalCount / sheetDataRows / writeDataRows + 1));

            //开始分批查询分次写入
            //注意这次的循环就需要进行嵌套循环了,外层循环是Sheet数目,内层循环是写入刺蛾如次数
            List<List<String>> dataList = new ArrayList<>();
            //提前插入记录表中
            //异步的方式
            // 创建异步执行任务:这里没有自定义线程池是为了图方便，实际上应该是使用自定义的
            ExecutorService executorService = Executors.newSingleThreadExecutor();
//            ByteArrayOutputStream byteArrayOutputStream= (ByteArrayOutputStream) finalOutputStream;
            executorService.submit(() -> {
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                ExcelWriter writer = EasyExcelFactory.write(out).build();
                try {
                    exportTask.setStatus(1);
                    exportTaskService.updateById(exportTask);
                    for (int i = 0; i < sheetNum; i++) {
                        //创建Sheet

                        WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();
                        //循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
                        for (int j = 0; j < (i != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                            System.out.println("x=" + i + " ,y=" + j);
                            dataList.clear();
                            PageHelper.startPage(j + 1 + oneSheetWriteCount * i, writeDataRows);
                            List<ActResultLog> reslultList = actResultLogMapper.findByPage100w();
                            if (!CollectionUtils.isEmpty(reslultList)) {
                                reslultList.forEach(item -> {
                                    dataList.add(Arrays.asList(item.getOnlineseqid(), item.getBusinessid(), item.getBecifno(), item.getIvisresult(), item.getCreatedby(), Calendar.getInstance().getTime().toString(), item.getUpdateby(), Calendar.getInstance().getTime().toString(), item.getRisklevel()));
                                });
                            }
                            //写数据
                            writer.write(dataList, writeSheet);
                        }
                    }
                    writer.finish();
                    out.flush();
                    ByteArrayOutputStream byteArrayOutputStream1 = new ByteArrayOutputStream();
                    byteArrayOutputStream1.writeTo(out);
                    //导出时间结束
                    long endTime = System.currentTimeMillis();
                    System.out.println("导出结束时间:" + endTime + "ms");
                    System.out.println("导出所用时间:" + (endTime - startTime) / 1000 + "秒");
                    exportTask.setStatus(2);


                    System.out.println(out.size());
                    // 文件上传到oss
                    InputStream fileInputStream = new ByteInputStream(out.toByteArray(), out.size());
//        MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
                    ObsClient obsClient = null;
                    PutObjectResult response1 = null;
                    try {
                        String endPoint = "http://obs.cn-east-3.myhuaweicloud.com";
                        String ak = "EWGYXHBAMGH2GMUTGFFZ";
                        String sk = "477LboFMzhzHPvPMN4p4B5fDopUhdCON0WsC49QB";
                        // 创建ObsClient实例
                        obsClient = new ObsClient(ak, sk, endPoint);
                        // 调用接口进行操作，例如上传对象
                        response1 = obsClient.putObject("dev-test-obs", "测试.xlsx", fileInputStream);  // localfile为待上传的本地文件路径，需要指定到具体的文件名
                        Map<String, Object> responseHeaders = response1.getResponseHeaders();
                        Map<String, Object> originalHeaders = response1.getOriginalHeaders();
                        String requestId = response1.getRequestId();
                        int statusCode = response1.getStatusCode();
                        String string = response1.toString();
                        String objectUrl = response1.getObjectUrl();
                        System.out.println(response1);
                        //优雅的创建带值的map集合
                        exportTask.setUrl(objectUrl);
                        exportTaskService.updateById(exportTask);
                        hashMap.put("url", objectUrl);
                    } catch (ObsException e) {
                        System.out.println("HTTP Code: " + e.getResponseCode());
                        System.out.println("Error Code:" + e.getErrorCode());
                        System.out.println("Error Message: " + e.getErrorMessage());

                        System.out.println("Request ID:" + e.getErrorRequestId());
                        System.out.println("Host ID:" + e.getErrorHostId());
                    } finally {
                        // 关闭ObsClient实例，如果是全局ObsClient实例，可以不在每个方法调用完成后关闭
                        // ObsClient在调用ObsClient.close方法关闭后不能再次使用
                        if (obsClient != null) {
                            // obsClient.close();
                        }
                    }


                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                    exportTask.setStatus(3);
                    exportTaskService.updateById(exportTask);
                } finally {
                    if (out != null) {
                        try {
                            out.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }

            });


        }
        System.out.println("success");

    }

    /**
     * 最终版本的导出，先记录日志，然后异步多线程版本导出，文件上传到obs,返回，invokeAll实现
     *
     * @param response
     * @return
     * @throws IOException
     */
    @GetMapping("dataExport300wOss5")
    public String dataExport300wOss5(HttpServletResponse response) throws IOException {
        //先插入一条记录，然后在导出完成的最后，上传到oss保存该记录
        ExportTask exportTask = new ExportTask();
        exportTask.setTaskName("前台系统传或者后台自定义");
        exportTask.setDr(0);
        exportTask.setCreateTime(new Date());
        exportTask.setCreatePerson("1");
        exportTask.setTaskParams("可以通过request获得");
        exportTask.setStatus(0);
        exportTask.setUserId(1L);
        exportTaskService.save(exportTask);
        HashMap<String, String> hashMap = new HashMap<String, String>();
        {

            long startTime = System.currentTimeMillis();
            System.out.println("导出开始时间:" + startTime);
            String fileName = new String(("excel100w").getBytes(), "UTF-8");

            //title
//            Table table = new Table(1);
//            List<List<String>> titles = new ArrayList<List<String>>();
//            titles.add(Arrays.asList("onlineseqid"));
//            titles.add(Arrays.asList("businessid"));
//            titles.add(Arrays.asList("becifno"));
//            titles.add(Arrays.asList("ivisresult"));
//            titles.add(Arrays.asList("createdby"));
//            titles.add(Arrays.asList("createddate"));
//            titles.add(Arrays.asList("updateby"));
//            titles.add(Arrays.asList("updateddate"));
//            titles.add(Arrays.asList("risklevel"));
//            table.setHead(titles);

            //模拟统计查询的数据数量这里模拟100w
            int count = 1000001;
            //记录总数:实际中需要根据查询条件进行统计即可
            Integer totalCount = actResultLogMapper.findActResultLogByCondations(count);
            totalCount = count;
            //每一个Sheet存放100w条数据
            Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;
            //每次写入的数据量
            Integer writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;
            //计算需要的Sheet数量
            Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            //计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
            Integer oneSheetWriteCount = sheetDataRows / writeDataRows;
            //计算最后一个sheet需要写入的次数
            Integer lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) : (totalCount / sheetDataRows / writeDataRows + 1));

            //开始分批查询分次写入
            //注意这次的循环就需要进行嵌套循环了,外层循环是Sheet数目,内层循环是写入刺蛾如次数
            List<List<String>> dataList = new ArrayList<>();
            //提前插入记录表中
            //异步的方式
            // 创建异步执行任务:这里没有自定义线程池是为了图方便，实际上应该是使用自定义的
            // 创建异步执行任务:这里没有自定义线程池是为了图方便，实际上应该是使用自定义的
            ExecutorService executorService1 = Executors.newSingleThreadExecutor();
//            ByteArrayOutputStream byteArrayOutputStream= (ByteArrayOutputStream) finalOutputStream;
            executorService1.submit(() -> {
                ExecutorService executorService = Executors.newFixedThreadPool(12);
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                ExcelWriter writer = EasyExcelFactory.write(out).build();
                try {
                    exportTask.setStatus(1);
                    exportTaskService.updateById(exportTask);
//                    Future<Integer> future = executorService.submit(() -> {
//                        for (int i = 0; i < sheetNum; i++) {
//                            //创建Sheet
//                            int finalI = i;
//
//                            WriteSheet   = EasyExcel.writerSheet(finalI, "模板" + finalI).build();
//                            //循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
//                            for (int j = 0; j < (finalI != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
//                                System.out.println("x=" + finalI + " ,y=" + j);
//                                dataList.clear();
//                                PageHelper.startPage(j + 1 + oneSheetWriteCount * finalI, writeDataRows);
//                                List<ActResultLog> reslultList = actResultLogMapper.findByPage100w();
//                                if (!CollectionUtils.isEmpty(reslultList)) {
//                                    reslultList.forEach(item -> {
//                                        dataList.add(Arrays.asList(item.getOnlineseqid(), item.getBusinessid(), item.getBecifno(), item.getIvisresult(), item.getCreatedby(), Calendar.getInstance().getTime().toString(), item.getUpdateby(), Calendar.getInstance().getTime().toString(), item.getRisklevel()));
//                                    });
//                                }
//                                //写数据
//                                writer.write(dataList, writeSheet);
//                            }
//                        }
//
//                        return 11;
//
//
//                    });
//                    Integer i = future.get();
//                    System.out.println(i);
                    List<MyCallableTsk> myCallableTsks = new ArrayList<>();
                    long start = System.currentTimeMillis();
                    for (int i = 0; i < sheetNum; i++) {
//                            //创建Sheet
                            int finalI = i;
                            Integer pageCount=(finalI != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount);
                        CountDownLatch cd = new CountDownLatch(pageCount);
                        WriteSheet writeSheet = EasyExcel.writerSheet(finalI, "模板" + finalI).build();
                        for (int j = 0; j < (finalI != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++){
                            System.out.println("x=" + finalI + " ,y=" + j+" ,cdcount="+cd.getCount());
                                dataList.clear();
                            myCallableTsks.add(new MyCallableTsk(j,writeDataRows,cd));
                        }
                        try {
                            List<Future<List<ActResultLog>>> futures = threadPoolExecutor.invokeAll(myCallableTsks);
                            for (Future<List<ActResultLog>> future : futures) {
                                List<ActResultLog> actResultLogs = future.get();
                                //写数据
                                writer.write(actResultLogs, writeSheet);
                            }
                        } catch (InterruptedException | ExecutionException e) {
                            e.printStackTrace();
                        }
                    }
                    List<ActResultLog> userEntityList = new ArrayList<>();

                    long end = System.currentTimeMillis();
                    System.out.println("主线程：" + Thread.currentThread().getName() + " , 导出指定数据成功 , 共导出数据：" + userEntityList.size() + " ,查询数据任务执行完毕共消耗时 ：" + (end - start) + "ms");

                    writer.finish();
                    out.flush();
                    ByteArrayOutputStream byteArrayOutputStream1 = new ByteArrayOutputStream();
                    byteArrayOutputStream1.writeTo(out);
                    //导出时间结束
                    long endTime = System.currentTimeMillis();
                    System.out.println("导出结束时间:" + endTime + "ms");
                    System.out.println("导出所用时间:" + (endTime - startTime) / 1000 + "秒");
                    exportTask.setStatus(2);


                    System.out.println(out.size());
                    // 文件上传到oss
                    InputStream fileInputStream = new ByteInputStream(out.toByteArray(), out.size());
//        MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
                    ObsClient obsClient = null;
                    PutObjectResult response1 = null;
                    try {
                        String endPoint = "http://obs.cn-east-3.myhuaweicloud.com";
                        String ak = "EWGYXHBAMGH2GMUTGFFZ";
                        String sk = "477LboFMzhzHPvPMN4p4B5fDopUhdCON0WsC49QB";
                        // 创建ObsClient实例
                        obsClient = new ObsClient(ak, sk, endPoint);
                        // 调用接口进行操作，例如上传对象
                        response1 = obsClient.putObject("dev-test-obs", "测试.xlsx", fileInputStream);  // localfile为待上传的本地文件路径，需要指定到具体的文件名
                        Map<String, Object> responseHeaders = response1.getResponseHeaders();
                        Map<String, Object> originalHeaders = response1.getOriginalHeaders();
                        String requestId = response1.getRequestId();
                        int statusCode = response1.getStatusCode();
                        String string = response1.toString();
                        String objectUrl = response1.getObjectUrl();
                        System.out.println(response1);
                        //优雅的创建带值的map集合
                        exportTask.setUrl(objectUrl);
                        exportTaskService.updateById(exportTask);
                        hashMap.put("url", objectUrl);
                    } catch (ObsException e) {
                        System.out.println("HTTP Code: " + e.getResponseCode());
                        System.out.println("Error Code:" + e.getErrorCode());
                        System.out.println("Error Message: " + e.getErrorMessage());

                        System.out.println("Request ID:" + e.getErrorRequestId());
                        System.out.println("Host ID:" + e.getErrorHostId());
                    } finally {
                        // 关闭ObsClient实例，如果是全局ObsClient实例，可以不在每个方法调用完成后关闭
                        // ObsClient在调用ObsClient.close方法关闭后不能再次使用
                        if (obsClient != null) {
                            // obsClient.close();
                        }
                    }


                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                    exportTask.setStatus(3);
                    exportTaskService.updateById(exportTask);
                } finally {
                    if (out != null) {
                        try {
                            out.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            });


//            }


        }
        System.out.println("success");
        return "success";
    }

    /**
     *  最终版本的导出，先记录日志，然后异步多线程版本导出，文件上传到obs,返回，这个简单，CountDownLatch实现
     * @param response
     * @throws IOException
     * @throws InterruptedException
     */
    @GetMapping("dataExport300wOss10")
    @ResponseBody
    public String dataExport300wOss10(HttpServletResponse response) throws IOException, InterruptedException {
        //先插入一条记录，然后在导出完成的最后，上传到oss保存该记录
        ExportTask exportTask = new ExportTask();
        exportTask.setTaskName("前台系统传或者后台自定义");
        exportTask.setDr(0);
        exportTask.setCreateTime(new Date());
        exportTask.setCreatePerson("1");
        exportTask.setTaskParams("可以通过request获得");
        exportTask.setStatus(0);
        exportTask.setUserId(1L);
        exportTaskService.save(exportTask);
        HashMap<String, String> hashMap = new HashMap<String, String>();
        {
            long startTime = System.currentTimeMillis();
            long start = System.currentTimeMillis();
            System.out.println("导出开始时间:" + startTime);
            String fileName = new String(("excel100w").getBytes(), "UTF-8");


            //模拟统计查询的数据数量这里模拟100w
            int count = 1000001;
            //记录总数:实际中需要根据查询条件进行统计即可
            Integer totalCount = actResultLogMapper.findActResultLogByCondations(count);
            totalCount = count;
            //每一个Sheet存放100w条数据
            Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;
            //每次写入的数据量
            Integer writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;
            //计算需要的Sheet数量
            Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            //计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
            Integer oneSheetWriteCount = sheetDataRows / writeDataRows;
            //计算最后一个sheet需要写入的次数
            Integer lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) : (totalCount / sheetDataRows / writeDataRows + 1));

            //开始分批查询分次写入
            //注意这次的循环就需要进行嵌套循环了,外层循环是Sheet数目,内层循环是写入刺蛾如次数

            //提前插入记录表中
            //异步的方式
            // 创建异步执行任务:这里没有自定义线程池是为了图方便，实际上应该是使用自定义的
            // 创建异步执行任务:这里没有自定义线程池是为了图方便，实际上应该是使用自定义的
            ExecutorService executorService1 = Executors.newFixedThreadPool(12);
            ExecutorService executorService2 = Executors.newFixedThreadPool(12);

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ExcelWriter writer = EasyExcelFactory.write(out).build();
            exportTask.setStatus(1);
            exportTaskService.updateById(exportTask);
            executorService2.execute(()->{
                for (int i = 0; i < sheetNum; i++){
                    int finalI = i;
                    List<List<String>> dataList = new ArrayList<>();
                    //创建Sheet
                    CountDownLatch cd=null;
                    Integer max=(finalI != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount);
                    cd = new CountDownLatch(max);
                    WriteSheet  writeSheet   = EasyExcel.writerSheet(finalI, "模板" + finalI).build();
                    //循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
                    for (int j = 0; j < (finalI != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                        int finalJ = j;
                        CountDownLatch finalCd = cd;
                        executorService1.execute(() -> {
                            System.out.println("x=" + finalI + " ,y=" + finalJ);
                            System.out.println("线程：" + Thread.currentThread().getName() + " , 读取数据  x"+finalI+",y=:"+ finalJ);
                            PageHelper.startPage(finalJ + 1 + oneSheetWriteCount * finalI, writeDataRows);
                            List<ActResultLog> reslultList = actResultLogMapper.findByPage100w();
                            if (!CollectionUtils.isEmpty(reslultList)) {
                                reslultList.forEach(item -> {
                                    dataList.add(Arrays.asList(item.getOnlineseqid(), item.getBusinessid(), item.getBecifno(), item.getIvisresult(), item.getCreatedby(), Calendar.getInstance().getTime().toString(), item.getUpdateby(), Calendar.getInstance().getTime().toString(), item.getRisklevel()));
                                });
                            }
                            //写数据
//
                            finalCd.countDown();
                        });

                    }
                    System.out.println("线程6666：" + Thread.currentThread().getName() + " , 写数据"+cd.getCount() );
                    try {
                        cd.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    //切记，这行代码不可以放到线程池里面跑，他是不支持异步操作的。
                    writer.write(dataList, writeSheet);
                    System.out.println("线程：" + Thread.currentThread().getName() + " , 写数据"+cd.getCount() );

                }

                long end = System.currentTimeMillis();
                System.out.println("主线程：" + Thread.currentThread().getName() + " , 导出指定数据成功 , 共导出数据："  + " ,查询数据任务执行完毕共消耗时 ：" + (end - start) + "ms");
                writer.finish();
                try {
                    out.flush();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                ByteArrayOutputStream byteArrayOutputStream1 = new ByteArrayOutputStream();
                try {
                    byteArrayOutputStream1.writeTo(out);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                //导出时间结束
                long endTime = System.currentTimeMillis();
                System.out.println("导出结束时间:" + endTime + "ms");
                System.out.println("导出所用时间:" + (endTime - startTime) / 1000 + "秒");
                exportTask.setStatus(2);


                System.out.println(out.size());
                // 文件上传到oss
                InputStream fileInputStream = new ByteInputStream(out.toByteArray(), out.size());
//        MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
                ObsClient obsClient = null;
                PutObjectResult response1 = null;
                try {
                    String endPoint = "http://obs.cn-east-3.myhuaweicloud.com";
                    String ak = "EWGYXHBAMGH2GMUTGFFZ";
                    String sk = "477LboFMzhzHPvPMN4p4B5fDopUhdCON0WsC49QB";
                    // 创建ObsClient实例
                    obsClient = new ObsClient(ak, sk, endPoint);
                    // 调用接口进行操作，例如上传对象
                    response1 = obsClient.putObject("dev-test-obs", "测试.xlsx", fileInputStream);  // localfile为待上传的本地文件路径，需要指定到具体的文件名
                    Map<String, Object> responseHeaders = response1.getResponseHeaders();
                    Map<String, Object> originalHeaders = response1.getOriginalHeaders();
                    String requestId = response1.getRequestId();
                    int statusCode = response1.getStatusCode();
                    String string = response1.toString();
                    String objectUrl = response1.getObjectUrl();
                    System.out.println(response1);
                    //优雅的创建带值的map集合
                    exportTask.setUrl(objectUrl);
                    exportTaskService.updateById(exportTask);
                    hashMap.put("url", objectUrl);
                    System.out.println(objectUrl);
                } catch (ObsException e) {
                    System.out.println("HTTP Code: " + e.getResponseCode());
                    System.out.println("Error Code:" + e.getErrorCode());
                    System.out.println("Error Message: " + e.getErrorMessage());

                    System.out.println("Request ID:" + e.getErrorRequestId());
                    System.out.println("Host ID:" + e.getErrorHostId());
                } finally {
                    // 关闭ObsClient实例，如果是全局ObsClient实例，可以不在每个方法调用完成后关闭
                    // ObsClient在调用ObsClient.close方法关闭后不能再次使用
                    if (obsClient != null) {
                        // obsClient.close();
                    }
                }

            });








//            }


        }
        System.out.println("success");
        return "success";
    }

    /**
     * 这个是没有bug的版本
     * @param page
     * @return
     */
    private TaskApiClient taskApiClient;
    @GetMapping("export")
    @ResponseBody
    public Void export(BldInviteRecordPage page) {

        // 先创建导出任务
        ExportTaskRequest excelExportTask = ExportTaskRequest.builder()
                .taskName("邀请记录导出")
                .taskCode(ExcelExportTaskTypeEnum.INVITE_RECORD.getDesc())
                .taskParams(JSONUtil.toJsonStr(page))
                .accountId("999")
                .status(TaskStatusEnum.STARTED.getCode()).build();
        Result<Long> longResult = taskApiClient.create(excelExportTask);
        Integer code = longResult.getStatus();
        if (Objects.equals(ResultCode.SUCCESS.getCode(), code)) {
            long startTime = System.currentTimeMillis();
            log.info("导出开始时间: {}", startTime);
            // 获取当前时间并格式化
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss");
            String formattedDateTime = now.format(formatter);
            String fileName = "邀请记录导出_"+ formattedDateTime;
            // 记录总数: 实际中需要根据查询条件进行统计即可
//            CommonPage<BldInviteRecordPageResponse> bldInviteRecordPageResponseCommonPage = CommonPage.restPage(this.page(page));
//            Long total = bldInviteRecordPageResponseCommonPage.getTotal();
            Integer totalCount = actResultLogMapper.findActResultLogByCondations(9999999);
            // 每一个Sheet存放100w条数据
            Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;
            // 每次写入的数据量
            Integer writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;
            // 计算需要的Sheet数量
            Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            // 计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
            Integer oneSheetWriteCount = sheetDataRows / writeDataRows;
            // 计算最后一个sheet需要写入的次数
            Integer lastSheetWriteCount = (totalCount % sheetDataRows) / writeDataRows;
            if (totalCount % sheetDataRows % writeDataRows != 0) {
                lastSheetWriteCount += 1;
            }
            // 开始分批查询分次写入
            // 注意这次的循环就需要进行嵌套循环了,外层循环是Sheet数目,内层循环是写入次数
//            List<List<BldInviteRecordPageResponse>> dataList = new ArrayList<>();
            List<ActResultLog> dataList = new ArrayList<>();

            // 提前插入记录表中
            // 异步的方式
            Integer finalLastSheetWriteCount = lastSheetWriteCount;
            CompletableFuture.runAsync(() -> {
                try (ByteArrayOutputStream out = new ByteArrayOutputStream();
                     ExcelWriter writer = EasyExcelFactory.write(out,BldInviteRecordPageResponse.class).build()) {
                    for (int i = 0; i < sheetNum; i++) {
                        // 创建Sheet
                        WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();
                        // 循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
                        for (int j = 0; j < (i != sheetNum - 1 ? oneSheetWriteCount : finalLastSheetWriteCount); j++) {
                            System.out.println("x=" + i + " ,y=" + j);
                            dataList.clear();
//                            PageHelper.startPage(j + 1 + oneSheetWriteCount * i, writeDataRows);
                            List<ActResultLog> reslultList = actResultLogMapper.findByPage100w();
                            if (!CollectionUtils.isEmpty(reslultList)) {
                                List<ActResultLog> bldInviteRecordPageResponses=new ArrayList<>();
                                reslultList.forEach(item -> {
                                    bldInviteRecordPageResponses.add(item);
                                });
                                dataList.addAll(bldInviteRecordPageResponses);
                            }
                            // 写数据
                            writer.write(dataList, writeSheet);
                        }
                    }
                    writer.finish();
                    out.flush();

                    // 导出时间结束
                    long endTime = System.currentTimeMillis();
                    log.info("导出结束时间: {}ms", endTime);
                    log.info("导出所用时间: {}秒", (endTime - startTime) / 1000);

// 文件上传到oss
                    InputStream fileInputStream = new ByteInputStream(out.toByteArray(), out.size());
//        MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
                    ObsClient obsClient = null;
                    PutObjectResult response1 = null;
                        String endPoint = "http://obs.cn-east-3.myhuaweicloud.com";
                        String ak = "EWGYXHBAMGH2GMUTGFFZ";
                        String sk = "477LboFMzhzHPvPMN4p4B5fDopUhdCON0WsC49QB";
                        // 创建ObsClient实例
                        obsClient = new ObsClient(ak, sk, endPoint);
                        // 调用接口进行操作，例如上传对象
                        response1 = obsClient.putObject("dev-test-obs", "测试.xlsx", fileInputStream);  // localfile为待上传的本地文件路径，需要指定到具体的文件名
                        Map<String, Object> responseHeaders = response1.getResponseHeaders();
                        Map<String, Object> originalHeaders = response1.getOriginalHeaders();
                        String requestId = response1.getRequestId();
                        int statusCode = response1.getStatusCode();
                        String string = response1.toString();
                        String objectUrl = response1.getObjectUrl();
                        System.out.println(response1);
                    // 创建成功后，异步导出
                    excelExportTask.setUrl(objectUrl);
                    excelExportTask.setId(longResult.getData());
                    excelExportTask.setStatus(TaskStatusEnum.FINISHED.getCode());
                    taskApiClient.update(excelExportTask);
                    log.info("导出成功");
                } catch (Exception e) {
                    log.error("导出任务失败", e);
                    excelExportTask.setStatus(TaskStatusEnum.FAILED.getCode());
                    taskApiClient.update(excelExportTask);
                }
            });
        }
        return null;
    }


    class MyCallableTsk implements Callable<List<ActResultLog>> {

        private CountDownLatch cd;

        private Integer pageNum;
        private Integer pageSize;

        public MyCallableTsk(Integer pageNum,Integer pageSize,CountDownLatch cd){
            this.pageNum =pageNum;
            this.pageSize =pageSize;
            this.cd=cd;
        }
        @Override
        public List<ActResultLog> call() throws Exception {
            System.out.println("线程：" + Thread.currentThread().getName() + " , 开始读取数据------");
            long start = System.currentTimeMillis();
            PageHelper.startPage(pageNum,pageSize);
            List<ActResultLog> userEntityList = actResultLogMapper.findByPage100w();
            System.out.println("线程：" + Thread.currentThread().getName() + " , 读取数据  "+userEntityList.size()+",页数:"+pageNum+ "耗时 ：" + (System.currentTimeMillis() - start)+ "ms");
            cd.countDown();
            System.out.println("剩余任务数  ===========================> " + cd.getCount());
            return userEntityList;
        }
    }


    /**
     * 分页下载，上传到oss，返回地址，这个会产出文件，不建议这样搞
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("downloadOssUrl1")
    @ResponseBody
    public PutObjectResult downloadOssUrl1(HttpServletResponse response) throws IOException {
        String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
        File file = new File(fileName + ".xlsx");
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            System.out.println("文件找不到异常");
            return null;
        }
        try (
                ExcelWriter excelWriter = EasyExcel.write(fileOutputStream, DownloadData.class).build()) {
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            // 去调用写入,这里我调用了五次，实际使用时根据数据库分页的总的页数来
            for (int i = 0; i < 5; i++) {
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<DownloadData> data = data();
                excelWriter.write(data, writeSheet);
            }
        }
// 文件上传到oss
        FileInputStream fileInputStream = new FileInputStream(file);
//        MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
        ObsClient obsClient = null;
        PutObjectResult response1 = null;
        try {
            String endPoint = "http://obs.cn-east-3.myhuaweicloud.com";
            String ak = "EWGYXHBAMGH2GMUTGFFZ";
            String sk = "477LboFMzhzHPvPMN4p4B5fDopUhdCON0WsC49QB";
            // 创建ObsClient实例
            obsClient = new ObsClient(ak, sk, endPoint);
            // 调用接口进行操作，例如上传对象
            response1 = obsClient.putObject("dev-test-obs", "测试.xlsx", fileInputStream);  // localfile为待上传的本地文件路径，需要指定到具体的文件名
            System.out.println(response1);
        } catch (ObsException e) {
            System.out.println("HTTP Code: " + e.getResponseCode());
            System.out.println("Error Code:" + e.getErrorCode());
            System.out.println("Error Message: " + e.getErrorMessage());

            System.out.println("Request ID:" + e.getErrorRequestId());
            System.out.println("Host ID:" + e.getErrorHostId());
        } finally {
            // 关闭ObsClient实例，如果是全局ObsClient实例，可以不在每个方法调用完成后关闭
            // ObsClient在调用ObsClient.close方法关闭后不能再次使用
            if (obsClient != null) {
                obsClient.close();
            }
            //手动删除
            file.delete();
        }
        return response1;
    }


    /**
     * 文件下载并且失败的时候返回json（默认失败了会返回一个有部分数据的Excel）
     *
     * @since 2.1.1
     */
    @GetMapping("downloadFailedUsingJson")
    public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 这里需要设置不关闭流
            EasyExcel.write(response.getOutputStream(), DownloadData.class).autoCloseStream(Boolean.FALSE).sheet("模板").doWrite(data());
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = MapUtils.newHashMap();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    /**
     * 文件上传
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link UploadData}
     * <p>
     * 2. 由于默认一行行的读取excel，所以需要创建excel一行一行的回调监听器，参照{@link UploadDataListener}
     * <p>
     * 3. 直接读即可
     */
    @PostMapping("upload")
    @ResponseBody
    public String upload(MultipartFile file) throws IOException {
        EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
        return "success";
    }


    /**
     * 这边模拟文件上传到文件服务器上，返回一个链接，需要读取这个链接然后转化为InputStream继续读取
     *
     * @param url
     * @return
     */
    @PostMapping("uploadUrl")
    @ResponseBody
    public String uploadUrl(String url) throws URISyntaxException, IOException {
        URL urls = new URL(url);
        URLConnection connection = urls.openConnection();
        InputStream is = connection.getInputStream();
        EasyExcel.read(is, UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
        return "success";
    }


    private List<DownloadData> data() {
        List<DownloadData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            DownloadData data = new DownloadData();
            data.setString("字符串" + 0);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

    public static void main(String[] args) {
        try {
            URL url = new URL("https://dev-test-obs1.obs.cn-east-3.myhuaweicloud.com:443/A3.png?AccessKeyId=6UUD4TZSFBS18T8C8WQ3&Expires=1692326606&x-obs-security-token=gQpjbi1ub3J0aC00iW2luvS1o5jsTuOUNU3CWu-bz-QcQ627MtphBq5NjQxb6PflQlWSjXoFS3_XefSFdF90xc24yJv4IHuBdtkqncxti42ARo2irqpNi9PqHh-xkMDCFedLEvTMWOqQQ4uZu_midHRs5fVyGkyUc2WOv8z_pXYc8IDEtnzJDlmh3UkgTYHVltMY7tKO7S8vST2z7P1PdlNU6fl0AQ9h_A_jCUdy9R2UJoxl2I_w3eNQxE0HBX6j0CYD6b5m6O2V54_r7xo8rCDuQYyyye_H6wRoBShf2PZyU0WwJHHMG0cfLMF86CL97hlaaXsQ8it_jvSle9Kk0aWIWVmtYLmw9nL--k7rU8qWAdVbJnXACacvtYsRFs5P0ZZtghwpiANWANW-WbfxrK8oCKPrlm_R1nXD20poIehOlGX0sJs-Y2Z4pAduDRojL1wQFkIMpnLsM1GR3nzlVXJtGmM8yWTjwM-t-pufjdLcakS-Ty1IbqXVVPiKGZAmbQ5QCPqtODLDuHA5Y2giJcnaSHLemcwTYUtI55g_ERCHFMKwxHBzT5zASKSOhwEUfuHakWvKfetwJ6pheI85sJ546gwWdCXCGT0LL0R1_fOcgVY8OSBZDRKCj_QbCFpgAN8_0exfmyHDnVHhv0byfA86Gh9uQDiJuUMtGrA4QozmeLT6lr-DHphlTMY1m3o-5DZ3CmVgK3GIZsgu7GVIQN7QG6F6VRRbinLfIBLd9nn8KJRK8VwDHZMeTOwP5nD_Yojz8A6ET335knjhu0_dUh6QDL1yaK87Z_YKUoiYzgOGmr4QnwGJivmSryjJd7PFVcX7htYtqL98AXIrKQ%3D%3D&Signature=DsjCuzi7FA02UQ56R4uXKbrnQ1o%3D");
//            File mainImageFile = new File("C:\\Users\\PC\\Desktop\\手持身份.jpg"); // 主图像

            BufferedImage mainImage = ImageIO.read(url);
            Graphics2D graphics = mainImage.createGraphics();
            URL url1 = new URL("https://dev-test-obs1.obs.cn-east-3.myhuaweicloud.com:443/xijie_shopQRCode_activity_1349331481694066743_1.jpg?AccessKeyId=XR2UCBY7IWHGVP87MJDH&Expires=1692326663&x-obs-security-token=gQpjbi1ub3J0aC00ibtUt0eEDDDHd1IVeLEvsdyf2nDoS89RxgFFSkKRxr4YDfP5kn1aJTEHzH6OxI7P_uS9nsPKg3SYkzEx4I5V7V4hmqQMS2o7hrz-JJT-oMp7_-reIEvAp6fIvg4XIn6DZilSXt54PqXHp3GeWbIb46gPqSePx3QEY4PS4RB0n96EB3j8VQlCoI_3O2dJa1BqozKCxwECrpbvLLHs4nwmxwipLucxyOHelzEQ_svkr5zmC3BoGhmbMeApF5oLahRmTeGZRtI6RFD0Q6BmLcFdtWxNz0J_EeD2btnyfdgZS8RB0T5lziGMHs42471jMAkegLP4qoaZr6nv2pug4B5yF60athWPK2aZuaz0KS0Dshf9tpP1QxzPKMxcDYWgW2_IR5Ecuj4F1L2q18bjpqNueEC2Z79xRqo3d_Okv9U1e0xF9TFCK98OOeD6u6cgg9lwCQ3u9Z-DQE_cyCVkQK0jnPGGvxCnLOpfW6sM63-pa4ulgm0mmk6qIIBxsw-bjiDjQa9HMzcNbzjnmczqDrVCcTAnPRspfOTBKoEdNMfKVR52H0udFZ6jDDL3x3eanufrIL-ATcvZPB0qiPFxxKeTgKl0aOrQG_6mzmgNXqfr4XoBH8aU0sgqxhDbE4F99__cDpX5-XvLkFeW64PrVb-3UdYrDTUfhzgZpHiPZ2wP4KxeLKOvsnoSVRnqKS7cEw92cI1OibiGcZ-hsYH0eT9zCDAVER5HQhl0PiUYs9NOKT3gFX55R1gU_IOQgRXvCB54snKCAwjVmMLwVTG_z2npYV3AT8DQUutCWznJ3OFPcHCwag1s4RTxD9TbiSVTk1HEQA%3D%3D&Signature=dehPeD6oWswwrZc7bO5DSRcQhK8%3D");
            File overlayImageFile = new File("C:\\Users\\PC\\Desktop\\王宽宽正面.jpg"); // 要叠加的图像
            BufferedImage overlayImage = ImageIO.read(url1);

            // 计算叠加图像的位置，这里我们将其放在主图像的中央
//            int x = (mainImage.getWidth() - overlayImage.getWidth()) / 2;
            int x = (mainImage.getWidth() - overlayImage.getWidth());

//            int y = (mainImage.getHeight() - overlayImage.getHeight()) / 2;
            int y = (mainImage.getHeight() - overlayImage.getHeight());
            // 将叠加图像画到主图像上
            graphics.drawImage(overlayImage, x, y, null);
            graphics.dispose();

            // 保存结果图像
//            File outputImageFile = new File("D:\\WinRAR\\5.png");
//            ImageIO.write(mainImage, "jpg", outputImageFile);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(mainImage, "jpg", out);
            System.out.println(out.toByteArray());
            System.out.println(out.size());
            getFileByBytes(out.toByteArray(), "D:\\WinRAR\\", "7.png");
// 文件上传到oss
            InputStream fileInputStream = new ByteInputStream(out.toByteArray(), out.size());
//        MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
            ObsClient obsClient = null;
            PutObjectResult response1 = null;
            try {
                String endPoint = "http://obs.cn-east-3.myhuaweicloud.com";
                String ak = "EWGYXHBAMGH2GMUTGFFZ";
                String sk = "477LboFMzhzHPvPMN4p4B5fDopUhdCON0WsC49QB";
                // 创建ObsClient实例
                obsClient = new ObsClient(ak, sk, endPoint);
                // 调用接口进行操作，例如上传对象
                response1 = obsClient.putObject("dev-test-obs", "61211.png", fileInputStream);  // localfile为待上传的本地文件路径，需要指定到具体的文件名
                System.out.println(response1);
            } catch (ObsException e) {
                System.out.println("HTTP Code: " + e.getResponseCode());
                System.out.println("Error Code:" + e.getErrorCode());
                System.out.println("Error Message: " + e.getErrorMessage());

                System.out.println("Request ID:" + e.getErrorRequestId());
                System.out.println("Host ID:" + e.getErrorHostId());
            } finally {
                // 关闭ObsClient实例，如果是全局ObsClient实例，可以不在每个方法调用完成后关闭
                // ObsClient在调用ObsClient.close方法关闭后不能再次使用
                if (obsClient != null) {
                    // obsClient.close();
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //将Byte数组转换成文件
    public static void getFileByBytes(byte[] bytes, String filePath, String fileName) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            File dir = new File(filePath);
            if (!dir.exists()) {// 判断文件目录是否存在
                dir.mkdirs();
            }
            file = new File(filePath + fileName);
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * @description: 超过线程池大小部分，将拒绝
     * @author sxp
     * @date 2021/10/21 14:30
     */
    @GetMapping("fixedThreadPool")
    @ResponseBody
    public void fixedThreadPool() {
        /**
         *  @description: 线程池使用步骤：1、创建线程池
         */
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //线程池要关闭，一般关闭我们放在finally中执行
        try {

            /**
             *  @description:线程池使用步骤：2、线程池执行线程
             */
//            for (int i = 0; i < 20; i++) {
//
//            }
            for (int i = 0; i < 20; i++) {
                int finalI = i;
                executorService.execute(() -> {

                            try {
                                Thread.sleep(1000L);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            System.out.println(Thread.currentThread().getName() + ":running-" + finalI);

                        }
                );
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /**
             *  @description:线程池使用步骤：3、关闭线程池
             */
            executorService.shutdown();
        }
    }



    @GetMapping("fixedThreadPool1")
    @ResponseBody
    public void fixedThreadPool1() {
        /**
         *  @description: 线程池使用步骤：1、创建线程池
         */
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        //线程池要关闭，一般关闭我们放在finally中执行
        try {

            /**
             *  @description:线程池使用步骤：2、线程池执行线程
             */
//            for (int i = 0; i < 20; i++) {
//
//            }
            for (int i = 0; i < 4; i++) {
                int finalI = i;
                CountDownLatch cd = new CountDownLatch(5);
                for (int j = 0; j < 5; j++){
                    executorService.execute(() -> {

                                try {
                                    Thread.sleep(1000L);
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                                System.out.println(Thread.currentThread().getName() + ":running-" + finalI);
                        cd.countDown();
                            }
                    );
                }
                cd.await();

            }

            System.out.println("1111");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /**
             *  @description:线程池使用步骤：3、关闭线程池
             */
            executorService.shutdown();
        }
    }



}
